32. Prueba de progreso 1ºA

Lee atentamente el enunciado de la prueba. El enunciado consiste en una serie de ejercicios que deben realizarse en un mismo archivo Python. Todas las funciones deben definirse en el mismo archivo y no debe incuirse ningún fragmento de código de prueba. Las entregas deberán ser trabajo original del alumno que realiza la entrega.

No se permite la comunicación con otras personas durante el examen. Se permiten libros, apuntes y búsquedas en Internet.

32.1. 1. Filtrado de señales

Dada una señal de tiempo discreto, definida como una secuencia de valores reales \(x = \{x_0, x_1, ... x_n\}\) se define la señal de salida de un filtro FIR como la secuencia de los valores:

\[y_n = \sum_{k=0}^{N-1} h_k\cdot x_{n-k}\]

donde \(h_k\) para \(k\in\{0..N-1\}\) son los coeficientes del filtro, y n es e índice del elemento correspondiente.

Nota: Una señal :math:`u = {u_0, u_1, ... u_{M-1}}` se modela como una lista de Python ``u`` con la secuencia de los números reales de la señal. Todos los demás elementos se asume que valen 0. Es decir :math:`u_k = 0, forall k notin {0..M-1}` siendo :math:`M` el número de elementos de ``u``.

  1. Definir una función ``fir_elem`` con tres parámetros. El primer parámetro es una lista ``h`` que contiene los coeficientes del filtro. El segundo parámetro es una lista ``x`` conteniendo una señal discreta. El tercer parámetro es un entero ``n`` que corresponde al índice del elemento de la señal de salida. La función debe devolver el resultado de calcular :math:`y_n` según la fórmula de arriba. Se recomienda emplear una función ``elem(u,k)`` que devuelve :math:`u_k`, es decir, el elemento k-ésimo de la señal que recibe como argumento. Se recuerda que :math:`k` puede estar fuera del rango de índices válidos para la lista ``u`` y que en ese caso el valor debe ser cero.

  2. Definir una función ``fir`` con dos parámetros. El primer parámetro es una lista ``h`` que contiene los coeficientes del filtro. El segundo parámetro es una lista ``x`` conteniendo una señal discreta. La función debe devolver la señal de salida del filtro FIR correspondiente. Es decir, debe devolver la lista de todos los elementos distintos de cero. La longitud de la salida de un filtro FIR es :math:`N+M-1` siendo :math:`N` la longitud de ``h`` y :math:`M` la longitud de ``x``.

  3. *Una característica interesante de los filtros FIR es que pueden tener respuesta de fase lineal, pero para eso los coeficiente de h deben cumplir una condición.

    \[ \begin{align}\begin{aligned}h_n = \pm h_{M-1-n}\ \ \ \forall n \in \{0..M-1\}\\Es decir, los coeficientes del fitro deben ser simétricos o\end{aligned}\end{align} \]

    antisimétricos. Según se cumpla la condición con signo positivo o negativo, y según el número de elementos de h sea par o impar, los filtros FIR se clasifican en cuatro tipos que se usan para fines diferentes. Definir una función tipo_fir que acepte un argumento h de tipo lista de reales y devuelva un valor entero de acuerdo a la siguiente tabla.*

Simetría de los coeficientes # Coefs Tipo
\(h(n) = h(M-1-n)\) Impar 1
\(h(n) = h(M-1-n)\) Par 2
\(h(n) = -h(M-1-n)\) Impar 3
\(h(n) = -h(M-1-n)\) Par 4
Sin simetría   0

32.1.1. Ejemplo de funcionamiento

x = [i % 10 for i in range(10)]
h = [1./3,1./3,1./3]
print fir(h, x)
print tipo_fir(h)

[0.0, 0.3333333333333333, 1.0, 1.9999999999999998, 2.9999999999999996, 4.0, 5.0, 6.0, 7.0, 7.999999999999999, 5.666666666666666, 3.0]
1

El primer ejercicio es aplicar directamente la fórmula con la única precaución de usar una función intermediaria para no indexar las listas fuera de rango.

def elem(u,k):
    return u[k] if k in range(len(u)) else 0.

def fir_elem(h,x,n):
    sum=0.
    for k in range(len(h)):
        sum += elem(h,k)*elem(x,n-k)
    return sum

El segundo ejercicio es más sencillo aún, basta con usar la función desarrollada en el ejercicio anterior en todo el rango de la señal de salida.

def fir(h,x):
    return [fir_elem(h,x,i) for i in range(len(h)+len(x)-1)]

También se puede hacer sin list comprehensions.

def fir(h,x):
    y = []
    for i in range(len(h)+len(x)-1):
        y.append(fir_elem(h,x,i))
    return y

El tercer ejercicio lo podemos hacer con list comprehensions y diccionarios. Calculamos los tres parámetros que permiten saber el tipo de filtro: si los coeficientes son simétricos, si son antisimétricos, y si el número de elementos en par o impar.

def tipo_fir(h):
    M = len(h)
    sim = all([h[i]==h[M-i-1] for i in range(M//2)])
    asim = all([h[i]==-h[M-i-1] for i in range(M//2)])
    par = (M % 2 == 0)
    return {
        (True,False,False) :1,
        (True,False,True)  :2,
        (False,True,False) :3,
        (False,True,True)  :4,
        (False,False,False):0,
        (False,False,True) :0,
        }[(sim,asim,par)]

También se puede hacer con if y sin list comprehensions.

def tipo_fir(h):
    if es_simetrico(h):
        return 2 if len(h) % 2 == 0 else 1
    if es_antisimetrico(h):
        return 4 if len(h) % 2 == 0 else 3
    return 0

def es_simetrico(h):
    M = len(h)
    for i in range(M//2):
        if h[i] != h[M-i-1]:
            return False
    return True

def es_antisimetrico(h):
    M = len(h)
    for i in range(M//2):
        if h[i] != -h[M-i-1]:
            return False
    return True

Ya solo falta probarlo.

x = [i % 10 for i in range(10)]
h = [1./3,1./3,1./3]
print(fir(h, x))
print(tipo_fir(h))
[0.0, 0.3333333333333333, 1.0, 1.9999999999999998, 2.9999999999999996, 4.0, 5.0, 6.0, 7.0, 7.999999999999999, 5.666666666666666, 3.0]
1

32.2. 2. Nota numérica

En muchas becas se calcula la nota media del expediente a partir de las notas alfabéticas. Así una nota de Matricula de honor computa como un 10.0, un Sobresaliente computa como un 9.0, un Notable como un 7.5, un Aprobado como un 5.5 y un Suspenso como un 3.0.

Define la función ``nota_media`` que calcula la nota media de un expediente a partir de sus valores alfabéticos. La función tiene un único argumento que es una lista de cadenas de texto, y debe devolver un número real con la nota media del expediente de acuerdo a lo descrito en el párrafo anterior.

Nota: Fíjate en que las cadenas de texto no llevan tildes y comienzan por un letra mayúscula. Deben ser exactamente así: ``Matricula de honor``, ``Sobresaliente``, ``Notable``, ``Aprobado``, ``Suspenso``.

32.2.1. Ejemplo de uso

expediente = ['Sobresaliente', 'Notable', 'Notable', 'Aprobado', 'Suspenso']
print nota_media(expediente)

6.5

La forma más fácil de hacerlo es con un diccionario.

def nota_media(e):
    sum=0.
    for i in e:
        sum += nota(i)
    return sum/len(e)

def nota(s):
    return {
        'Matricula de honor':10.,
        'Sobresaliente':9.,
        'Notable':7.5,
        'Aprobado':5.5,
        'Suspenso':3
        }[s]

expediente = ['Sobresaliente', 'Notable', 'Notable', 'Aprobado', 'Suspenso']
print(nota_media(expediente))
6.5

32.3. Rúbrica de evaluación

Puntos totales: 10 puntos:

  • 1.1 Función fir_elem: 2.5 puntos
  • 1.2 Función fir: 2.5 puntos
  • 1.3 Función tipo_fir: 2.5 puntos
  • 2 Función nota_media: 2.5 puntos

Penalizaciones:

  • Errores de sintaxis: 100% de los puntos de la función
  • Errores de ejecución (excepciones): 50% de los puntos de la función
  • Errores en límites de recorridos: 20% de los puntos de la función
  • Código repetitivo: 10% de los puntos de la función
Next Section - 33. Prueba de progreso 1ºB